/*
    接收消息的回调函数待优化，目前只是订阅一个主题，如订阅多个主题该如何处理
    （1） 直接在消息回调函数中处理
            适合主题少，处理简单的情况
    （2）使用回调函数
            typedef void (*TopicHandler)(const char* topic, const char* payload, int payload_len);

            struct {
                char *topicName;
                TopicHandler handler;
            }TopicHandlerMap;

            // 全局处理映射表
            TopicHandlerMap handler_map[] = {
                {"sensors/temperature", handleTemperature},
                {"sensors/humidity", handleHumidity},
                {"devices/+/status", handleDeviceStatus},
            };

            遍历映射表，
    （3）使用消息队列  如何使用？
    （4）使用线程 ？
*/



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "MQTTClient.h"

#define SERVICE_IP "tcp://localhost:1883"
#define CLIENT_ID "test1"

#define HELLO_TOPIC "dt/hello"
#define TEXT_TOPIC  "dt/testTopic"
#define SEND_TOPIC  "dt/send"


// 断开连接时的回调函数
void connectionLost(void* context, char* cause)
{
    printf("断开连接，原因为：%s\n", cause);
}

// 接收消息回调函数 
int receiveMessage(void* context, char* topicName, int topicLen, MQTTClient_message* message)
{
    printf("Message arrived\n");
    printf("topic: %s\n", topicName);
    printf("message: <%d>%s\n", message->payloadlen, (char *)message->payload);
    
    // 这两个一定要释放
    MQTTClient_freeMessage(&message); //释放内存
    MQTTClient_free(topicName); //释放内存

    return 1;
}

// 发送消息回调函数
void deliveryComplete(void* context, MQTTClient_deliveryToken dt)
{
    printf("Message with token value %d delivery confirmed\n", dt);
}

int main(int argc, char *argv[]) 
{
    MQTTClient client;
    // 消息主题结构体一定要初始化，否则链接时会报错，报错返回码为 -8
    MQTTClient_willOptions will = MQTTClient_willOptions_initializer; 
    MQTTClient_connectOptions connectOption = MQTTClient_connectOptions_initializer;
    MQTTClient_message pubmsg = MQTTClient_message_initializer;
    MQTTClient_deliveryToken token;


    int ret;

    // 创建客户端
    ret = MQTTClient_create(&client, SERVICE_IP, CLIENT_ID, MQTTCLIENT_PERSISTENCE_NONE, NULL);
    if (ret != MQTTCLIENT_SUCCESS) {
        printf("客户端创建，失败码：%d\n", ret);
        goto exit;
    } 

    // 设置回调函数 设置回调函数 一定要在链接服务端之前 否则无法接收到订阅的主题信息
    ret = MQTTClient_setCallbacks(client, NULL, connectionLost, receiveMessage, deliveryComplete);
    if (ret != MQTTCLIENT_SUCCESS) {
        printf("设置回调函数失败，失败码：%d\n", ret);
        goto destroy_exit;
    }

    // 遗嘱
    will.message = "Abnormally dropped";    // 消息
    will.qos = 0;                           // 等级 最多发一次
    will.topicName = "dt/willTopic";        // 主题名称
    will.retained = 1;                      // 保留消息

    connectOption.will = &will; 
    connectOption.keepAliveInterval = 30;   // 心跳间隔
    connectOption.cleansession = 0;         // 不清楚之前会话

    // 连接服务端
    ret = MQTTClient_connect(client, &connectOption);
    if (ret != MQTTCLIENT_SUCCESS) {
        printf("连接服务端失败，失败码：%d\n", ret);
        goto destroy_exit;
    } 


    // 发布消息
    pubmsg.payload = "online";
    pubmsg.payloadlen = 6;
    pubmsg.qos = 0;
    pubmsg.retained = 1;
    ret = MQTTClient_publishMessage(client, TEXT_TOPIC, &pubmsg, &token);
    if (ret != MQTTCLIENT_SUCCESS) {
        printf("消息发布失败，失败码：%d\n", ret);
        goto disconnect_exit;
    }

    // 订阅主题
    ret = MQTTClient_subscribe(client, HELLO_TOPIC, 0);
    if (ret != MQTTCLIENT_SUCCESS) {
        printf("主题订阅失败，失败码：%d\n", ret);
        goto disconnect_exit;
    }

    while(1) {
        MQTTClient_message sendmsg = MQTTClient_message_initializer;
        sendmsg.payload = "hello world";
        sendmsg.payloadlen = 11;
        sendmsg.qos = 0;
        sendmsg.retained = 1;
        ret = MQTTClient_publishMessage(client, SEND_TOPIC, &sendmsg, NULL);
        if (ret != MQTTCLIENT_SUCCESS) {
            printf("消息发布失败，失败码：%d\n", ret);
            goto unsubscribe_exit;
        }

        sleep(30);
    }

unsubscribe_exit:
    if (MQTTCLIENT_SUCCESS !=
        (ret = MQTTClient_unsubscribe(client, HELLO_TOPIC))) {
        printf("Failed to unsubscribe, return code %d\n", ret);
        ret = EXIT_FAILURE;
    }

disconnect_exit:
    if (MQTTCLIENT_SUCCESS !=
        (ret = MQTTClient_disconnect(client, 10000))) {
        printf("Failed to disconnect, return code %d\n", ret);
        ret = EXIT_FAILURE;
    }

destroy_exit:
    MQTTClient_destroy(&client);

exit:
    return ret;
}

